//1. 灯光拍一次场景
// 从0-1的单通道值，1是最远点
//开启深度写入和深度测试

//2.渲染每个物体片元，判断片元深度值是否比上一步灯光拍摄深度图深度值大，
// 主摄像机是0.9，灯光摄像机是0.4，灯光坐标系

1.unity如何渲染出深度图？
//灯光阴影模式是渲染阴影
//FrameDebug那里的ShadowPrenderShowMap步骤生成
//不会生成多个深度图，cascades（卡斯k）
//级联，远贴图粗糙，近贴图惊喜，在项目设置那里‘
//ScreenSpaceShadowCascades
//Bultin Shader下载功能
//摄像机位置的深度图Texture，和shadowMapTexture
//在灯光space
Shader "BaseWithPlaneShadow"
//1.压扁
//2.投影偏移
//3.投影拉长
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _Color ("Color", Color) = (1,1,1,1)
        _ShadowOffset ("Shadow Offset(XZ),shadowHeigth(Y)", Vector) = (0,0,0,0)
    }
// 把外面的lighting castShadows off，就不会有角色阴影同时有
    SubShader
    {
        LOD 400 //300就要用这个shader，从上往下找
        //消除两个阴影同时存在的pass
        Pass
        {
            Name "NoShadowCaster" //，就不会有角色阴影同时有
            Tags
            {
                "LightMode" = "ShadowCaster" // 告诉unity，这是一个阴影投射器
            }
            ZTest Never // 不进行深度测试,不会产生阴影士兵
            ColorMask 0 // 关闭颜色通道，只渲染深度值
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #include "Lighting.cginc"
            #include "AutoLight.cginc"
            struct appData
            {
                float4 vertex : POSITION; //pos in local space 自己的空间
                float2 uv : TEXCOORD0;
            }; //结构体后一定加分号
            struct v2f
            {
                float4 pos : SV_POSITION;// pos in screen space 屏幕空间
                float2 uv : TEXCOORD0;
            };
            sampler2D _MainTex;
            float4 _MainTex_ST;
            v2f vert(appData v)// 输入
            {
                v2f o = (v2f)0;// 输出
                // o.pos  = UnityObjectToClipPos(v.vertex); // 顶点坐标转换;
                return o;
            }

            fixed frag(v2f i) : SV_Target
            {
                // fixed4 col = tex2D(_MainTex, i.uv);
                return col;
            }
            ENDCG

        }

        Pass
        {
            Name "BasePass" //第一个pass画角色
            Tags
            {
                //阴影能够打在角色上
                //Unity必须找到ForwardBase的Pass，才知道渲染阴影的pass是哪一个
                "LightMode" = "ForwardBase" // 告诉unity，这是一个前向渲染器
            }

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            //宏定义
            #pragma multi_compile_fwdbase // 宏定义
            //在外面skip unused shader_features

            #include "UnityCG.cginc"
            #include "Lighting.cginc"
            #include "AutoLight.cginc"

            

            struct appData
            {
                float4 pos : POSITION; //pos in local space 自己的空间
                float2 uv : TEXCOORD0;
            }; //结构体后一定加分号

            struct v2f
            {
                float4 pos : SV_POSITION;// pos in screen space 屏幕空间
                float4 worldPos:TEXCOORD1;
                float2 uv : TEXCOORD0;
                UNITY_SHADOW_COORDS(5) //宏定义,贴到第一个通道，避免和TEXCOORD0冲突
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;

            v2f vert(appData v)// 输入
            {
                v2f o;// 输出
                o.vertex  = UnityObjectToClipPos(v.vertex); // 顶点坐标转换;
                output.uv = TRANSFORM_TEX(v.uv,_MainTex) ;// 纹理坐标直接传递给frag，不用乘
                // return o;
                TRANSFER_SHADOW(o) //计算灯光空间下顶点位置
                return o;
                //为什么不在自身模型空间压扁，而是在世界空间压扁？
                //因为角色会旋转，压扁的阴影也跟着转
            }

            // 顶点的输出是像素的输入 
            half4 frag(v2f i) : SV_Target
            {
                half atten; //光照衰减
                // half4 col = tex2D(_MainTex, output.uv);
               UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos); //计算光照衰减
            //    return atten;

                half4 col = tex2D(_MainTex, i.uv);
                return col * atten;

            }
            ENDCG
    
        }

        Pass
        {
            Name "ShadowCaster91Make"
            Tags
            {
                "LightMode" = "ShadowCaster" // 告诉unity，这是一个阴影投射器
            }
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"
            #include "Lighting.cginc"
            #include "AutoLight.cginc"

            //点光源要立方体贴图，在untiy/cginc里面
            struct appData
            {
                //这个vertex就是位置，必须要这样叫！！！unity预定义的宏就是用这个vertex名称
                float4 vertex : POSITION; //pos in local space 自己的空间
                float2 uv : TEXCOORD0;
                half3 normal:NORMAL;//fixed只有-1到1
                //urp不支持fixed类型

            }; //结构体后一定加分号

            struct v2f
            {
                float2 uv : TEXCOORD0;
                //macro definition
                V2F_SHADOW_CASTER;//宏定义
                //pos:SV_POSITION  | [vec :TEXCOORD0]
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;

            v2f vert(appData v)// 输入,必须要叫v因为宏定义用到
            {
                v2f o;// 输出
                o.pos  = UnityObjectToClipPos(v.vertex); // 顶点坐标转换;
                o.uv = TRANSFORM_TEX(v.uv,_MainTex) ;// 纹理坐标直接传递给frag，不用乘
                TRANSFER_SHADOW_CASTER_NORMALOFFSET(o) //计算灯光空间下顶点位置
                return o;
                //为什么不在自身模型空间压扁，而是在世界空间压扁？
                //因为角色会旋转，压扁的阴影也跟着转
                // Shadow Ance 阴影摩尔纹，unity有法线方向Bias偏移
                //peter Panning
            }

            // 顶点的输出是像素的输入 
            half4 frag(v2f i) : SV_Target
            {
                // half4 col = tex2D(_MainTex, output.uv);
                // half4 col = tex2D(_MainTex, i.uv);
                // return col;
                SHADOW_CASTER_FRAGMENT(i);
            }
            ENDCG

        }

    
    }

// 平面阴影（如果LOD200 会穿插）
    SubShader
    {
            //半透明物体，先画远再画近
            //不透明：从前往后（节省性能）
        Tags { "RenderType"="Opaque" }
        LOD 200

        Pass
        {
            Name "BasePass" //第一个pass画角色

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"
            #include "Lighting.cginc"
            #include "AutoLight.cginc"

            

            struct appData
            {
                float4 pos : POSITION; //pos in local space 自己的空间
                float2 uv : TEXCOORD0;
            }; //结构体后一定加分号

            struct v2f
            {
                float4 pos : SV_POSITION;// pos in screen space 屏幕空间
                float2 uv : TEXCOORD0;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;

            v2f vert(appData v)// 输入
            {
                v2f o;// 输出
                o.vertex  = UnityObjectToClipPos(v.vertex); // 顶点坐标转换;
                output.uv = TRANSFORM_TEX(v.uv,_MainTex) ;// 纹理坐标直接传递给frag，不用乘
                return o;
                //为什么不在自身模型空间压扁，而是在世界空间压扁？
                //因为角色会旋转，压扁的阴影也跟着转
            }

            // 顶点的输出是像素的输入 
            half4 frag(v2f i) : SV_Target
            {
                // half4 col = tex2D(_MainTex, output.uv);
                half4 col = tex2D(_MainTex, i.uv);
                return col;
            }
            ENDCG
    
        }

        Pass
        {
            Name "PlanarShadow"
            Blend SrcAlpha OneMinusSrcAlpha //混合模式 1-源颜色
            Stencil
            {
                Ref 1 // 1 为1的地方才渲染
                Comp NotEqual // 比较方式
                Pass Replace // 当模版测试通过，根据stencilOperation的值，对模版缓冲处理
                Fail Keep // 测试失败保持模版值
            }

            // 避免穿插,shader可以自己制定offset，z-fighting
            // Offset Factor,Units
            Offset -1,-1



            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            

            struct VertInput
            {
                float4 pos : POSITION; //pos in local space 自己的空间
                float2 uv : TEXCOORD0;
            }; //结构体后一定加分号

            struct v2f
            {
                float4 pos : SV_POSITION;// pos in screen space 屏幕空间
                float2 uv : TEXCOORD0;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            float4 _Color;
            float4 _ShadowOffset;

            v2f vert(appData v)// 输入
            {
                v2f o;// 输出
                // o.vertex = UnityObjectToClipPos(input.pos); // 顶点坐标转换
              //全部变成脚底的高度值
              float4 vWorld = mul(unity_ObjectToWorld, v.vertex);//自身坐标变到世界空间
              vWorld.xz += _ShadowOffset.xz * (vWorld.y - _ShadowOffset.y);//投影偏移
              vWorld.y = _ShadowOffset.y;//变到脚底的高度值

                // Window ->Analysis ->Frame Debugger //TA的性能分析工具
                // 还差摄像机和投影矩阵变换！PV矩阵组合，就不用乘两次
                o.vertex  = mul(UNITY_MATRIX_VP, vWorld);
                o.uv = v.uv;// 纹理坐标直接传递给frag，不用乘
                return output;
                //为什么不在自身模型空间压扁，而是在世界空间压扁？
                //因为角色会旋转，压扁的阴影也跟着转
            }

            // 顶点的输出是像素的输入 
            half4 frag(v2f i) : SV_Target
            {
                // half4 col = tex2D(_MainTex, output.uv);
                // half4 col = tex2D(_MainTex, i.uv);
                return float(vec3(0.0),0.5);//黑色
            }
            ENDCG
    
        }
      
    }
}
//表面函数作为模型顶点作为输入
// 优缺点】
// 不打灯：不计算光照
// 1。如何实现多光源屏幕阴影？
// 2。如何实现阴影渐隐